package com.ziytek.web.citizen.listener;

import com.google.gson.Gson;
import com.ziytek.web.citizen.caffeine.CachePutOrGet;
import com.ziytek.web.citizen.config.caffeine.CaffeineUtil;
import com.ziytek.web.citizen.constant.NumConstant;
import com.ziytek.web.citizen.enums.StepTypeNoEnum;
import com.ziytek.web.citizen.event.CommandIssuedSuccessEvent;
import com.ziytek.web.citizen.mapper.CommandIssuedMapper;
import com.ziytek.web.citizen.model.bean.CommandConfirmation;
import com.ziytek.web.citizen.model.bean.CommandIssuedSuccess;
import com.ziytek.web.citizen.pojo.CommandIssued;
import com.ziytek.web.citizen.util.CabinetNoUtil;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.event.EventListener;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Service;
import org.springframework.util.Assert;
import org.springframework.util.CollectionUtils;

import java.util.Map;

/**
 * @author fengh
 */
@Slf4j
@Service
@RequiredArgsConstructor(onConstructor = @__(@Autowired))
public class CommandIssuedSuccessEventListener {

    private final Gson gson;
    private final CachePutOrGet cachePutOrGet;
    private final CommandIssuedMapper commandIssuedMapper;

    @Async("myAsyncExecutor")
    @EventListener
    public void commandIssuedSuccessEventListener(CommandIssuedSuccessEvent event) {
        Assert.notNull(event, "CommandIssuedSuccessEvent event must not be null");
        Assert.notNull(event.getCommandIssuedSuccess(), "CommandIssuedSuccessEvent commandIssuedSuccess must not be null");
        log.info("工步指令执行成功CommandIssuedSuccessEvent: {}", gson.toJson(event));

        commandIssuedSuccessEventProcessor(event, NumConstant.INT_ONE);
    }

    /**
     * 给设备下发指令成功后，等等设备指令响应结果，根据响应结果处理缓存数据
     */
    private void commandIssuedSuccessEventProcessor(CommandIssuedSuccessEvent event, int count) {
        var command = event.getCommandIssuedSuccess();
        var cabinetNo = CabinetNoUtil.getCabinetNo(String.valueOf(command.getReqCabinetNo()));

        var setType = command.getSetType();
        // 搁置静置指令
        var isSl = StringUtils.isNotEmpty(setType) && StepTypeNoEnum.STEP_TYPE_SL_NUM.getCode().equalsIgnoreCase(setType);

        // 指令下发结果确认 查询指令下发结果缓存
        //var com = cachePutOrGet.getCommandConfirmationCacheStartWith(cabinetNo);
        // 非搁置指令，指令下发结果需要确认是否成功
        var notSlAndNotComSuccess = true;
        var idKey = CaffeineUtil.COMMAND_CONFIRMATION_ID
                + cabinetNo
                + CaffeineUtil.CACHE_KEY_CONNECTION_SYMBOL
                + command.getBoardNo()
                + CaffeineUtil.CACHE_KEY_CONNECTION_SYMBOL
                + command.getChannelNo()
                + CaffeineUtil.CACHE_KEY_CONNECTION_SYMBOL
                + command.getStepId();

        var msgId = "";
        CommandConfirmation comConfig = null;
        Map<String, String> idsMap = cachePutOrGet.getCommandIdsCacheStartWith(idKey);
        if (!CollectionUtils.isEmpty(idsMap)) {
            for (Map.Entry<String, String> entry : idsMap.entrySet()) {
                msgId = entry.getValue();
                var cfKey = CaffeineUtil.COMMAND_CONFIRMATION + cabinetNo + CaffeineUtil.CACHE_KEY_CONNECTION_SYMBOL + msgId;
                comConfig = CaffeineUtil.get(cfKey);
                notSlAndNotComSuccess = !isSl && (comConfig == null || comConfig.getSuccess() == null || !comConfig.getSuccess());
                // 指令下发返回成功结果确认，跳出循环
                if (!notSlAndNotComSuccess) {
                    CaffeineUtil.delete(cfKey);
                    break;
                }
            }
        }

        // 指令下发结果未准确，等待1s后重试
        if (notSlAndNotComSuccess) {
            //log.info("CommandConfirmation not found in cache, wait for 1s, retry cabinetNo: {}", cabinetNo);
            // 延时等待
            try {
                Thread.sleep(NumConstant.LONG_ONE_THOUSAND * Math.max(count / NumConstant.INT_TWO, NumConstant.INT_ONE));
            } catch (InterruptedException e) {
                log.error("InterruptedException: ", e);
            }

            if (count > NumConstant.INT_FIVE * NumConstant.INT_FOUR) {
                log.info("工步指令执行成功但找不到缓存CommandConfirmation not found in cache, retry count: {}, cabinetNo: {}", count, cabinetNo);
                return;
            }
            count++;

            // 延时等待时间结束，重试
            commandIssuedSuccessEventProcessor(event, count);
            return;
        }

        log.info("工步指令执行成功而且查询有缓存数据CommandConfirmation found in cache: {}", gson.toJson(comConfig));

        // 上一个工艺指令缓存删除key
        var deleteKey = CaffeineUtil.getCacheKey(Integer.parseInt(command.getReqCabinetNo()), command.getBoardNo(), command.getChannelNo(), event.getLastOrder());
        var deleteKeyTwo = CaffeineUtil.getCacheKey(Integer.parseInt(command.getReqCabinetNo()), command.getBoardNo(), command.getChannelNo());
        // 停止指令下发成功，删除本地缓存数据
        if (NumConstant.LONG_LT_ZERO == command.getStepId()) {
            // time = NumConstant.LONG_ZERO;
            CaffeineUtil.delete(CaffeineUtil.CACHE_KEY_COMMAND + deleteKey);
            CaffeineUtil.delete(CaffeineUtil.CACHE_KEY_COMMAND + deleteKeyTwo);
            log.info("终止指令下发响应成功,删除操作缓存key:{},keyTwo:{}", deleteKey, deleteKeyTwo);
        }

        // 持久化下发指令数据
        commandIssuedCreate(command);
    }

    private void commandIssuedCreate(CommandIssuedSuccess command) {
        var time = System.currentTimeMillis();
        var commandIssued = new CommandIssued();
        commandIssued.setCreateTime(time);
        commandIssued.setUpdateTime(time);
        commandIssued.setFirstDataTime(time);
        commandIssued.setOrderNo(command.getOrder());
        commandIssued.setStepId(command.getStepId());
        commandIssued.setBoardNo(command.getBoardNo());
        commandIssued.setChannelNo(command.getChannelNo());
        commandIssued.setFinishedStatus(NumConstant.INT_ZERO);
        commandIssued.setSettingsDetail(command.getSettings());
        commandIssued.setHasExecutedTime(NumConstant.LONG_ZERO);
        commandIssued.setExpectedTime(command.getExpectedTimeSecond());
        commandIssued.setCabinetNo(CabinetNoUtil.getCabinetNo(command.getReqCabinetNo()));
        commandIssuedMapper.insert(commandIssued);
    }
}
